#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MCP Calculator Server - History and Cache Management

历史记录和缓存管理模块
"""

import json
import sqlite3
import threading
from typing import Dict, Any, List, Optional, Union
from datetime import datetime, timedelta
from dataclasses import dataclass, asdict
from pathlib import Path
import hashlib
import pickle
from collections import OrderedDict

@dataclass
class CalculationRecord:
    """计算记录"""
    id: Optional[int] = None
    expression: str = ""
    result: Union[float, str] = ""
    operation_type: str = ""
    success: bool = True
    error_message: Optional[str] = None
    timestamp: datetime = None
    duration: float = 0.0
    input_hash: str = ""
    metadata: Dict[str, Any] = None
    
    def __post_init__(self):
        if self.timestamp is None:
            self.timestamp = datetime.now()
        if self.metadata is None:
            self.metadata = {}
        if not self.input_hash:
            self.input_hash = self._generate_hash()
    
    def _generate_hash(self) -> str:
        """生成输入哈希"""
        content = f"{self.expression}:{self.operation_type}"
        return hashlib.md5(content.encode()).hexdigest()
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        data = asdict(self)
        data['timestamp'] = self.timestamp.isoformat() if self.timestamp else None
        return data
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'CalculationRecord':
        """从字典创建"""
        if 'timestamp' in data and data['timestamp']:
            data['timestamp'] = datetime.fromisoformat(data['timestamp'])
        return cls(**data)

class LRUCache:
    """LRU缓存实现"""
    
    def __init__(self, max_size: int = 1000):
        self.max_size = max_size
        self.cache = OrderedDict()
        self._lock = threading.Lock()
    
    def get(self, key: str) -> Optional[Any]:
        """获取缓存值"""
        with self._lock:
            if key in self.cache:
                # 移动到末尾（最近使用）
                value = self.cache.pop(key)
                self.cache[key] = value
                return value
            return None
    
    def put(self, key: str, value: Any):
        """设置缓存值"""
        with self._lock:
            if key in self.cache:
                # 更新现有值
                self.cache.pop(key)
            elif len(self.cache) >= self.max_size:
                # 删除最久未使用的项
                self.cache.popitem(last=False)
            
            self.cache[key] = value
    
    def remove(self, key: str) -> bool:
        """删除缓存项"""
        with self._lock:
            if key in self.cache:
                del self.cache[key]
                return True
            return False
    
    def clear(self):
        """清空缓存"""
        with self._lock:
            self.cache.clear()
    
    def size(self) -> int:
        """获取缓存大小"""
        return len(self.cache)
    
    def keys(self) -> List[str]:
        """获取所有键"""
        with self._lock:
            return list(self.cache.keys())
    
    def get_stats(self) -> Dict[str, Any]:
        """获取缓存统计"""
        with self._lock:
            return {
                'size': len(self.cache),
                'max_size': self.max_size,
                'usage_rate': len(self.cache) / self.max_size if self.max_size > 0 else 0
            }

class HistoryManager:
    """历史记录管理器"""
    
    def __init__(self, db_path: str = "calculator_history.db", max_memory_records: int = 1000):
        self.db_path = Path(db_path)
        self.max_memory_records = max_memory_records
        self.memory_records = []
        self.result_cache = LRUCache(max_size=500)
        self._lock = threading.Lock()
        
        self._init_database()
    
    def _init_database(self):
        """初始化数据库"""
        self.db_path.parent.mkdir(parents=True, exist_ok=True)
        
        with sqlite3.connect(self.db_path) as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS calculations (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    expression TEXT NOT NULL,
                    result TEXT NOT NULL,
                    operation_type TEXT NOT NULL,
                    success BOOLEAN NOT NULL,
                    error_message TEXT,
                    timestamp TEXT NOT NULL,
                    duration REAL NOT NULL,
                    input_hash TEXT NOT NULL,
                    metadata TEXT
                )
            """)
            
            # 创建索引
            conn.execute("CREATE INDEX IF NOT EXISTS idx_timestamp ON calculations(timestamp)")
            conn.execute("CREATE INDEX IF NOT EXISTS idx_input_hash ON calculations(input_hash)")
            conn.execute("CREATE INDEX IF NOT EXISTS idx_operation_type ON calculations(operation_type)")
            conn.execute("CREATE INDEX IF NOT EXISTS idx_success ON calculations(success)")
    
    def add_record(self, record: CalculationRecord) -> int:
        """添加计算记录"""
        with self._lock:
            # 添加到内存
            self.memory_records.append(record)
            if len(self.memory_records) > self.max_memory_records:
                self.memory_records.pop(0)
            
            # 添加到数据库
            with sqlite3.connect(self.db_path) as conn:
                cursor = conn.execute("""
                    INSERT INTO calculations 
                    (expression, result, operation_type, success, error_message, 
                     timestamp, duration, input_hash, metadata)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                """, (
                    record.expression,
                    str(record.result),
                    record.operation_type,
                    record.success,
                    record.error_message,
                    record.timestamp.isoformat(),
                    record.duration,
                    record.input_hash,
                    json.dumps(record.metadata) if record.metadata else None
                ))
                
                record.id = cursor.lastrowid
                return record.id
    
    def get_records(self, limit: int = 100, offset: int = 0, 
                   operation_type: Optional[str] = None,
                   success_only: bool = False,
                   start_time: Optional[datetime] = None,
                   end_time: Optional[datetime] = None) -> List[CalculationRecord]:
        """获取计算记录"""
        conditions = []
        params = []
        
        if operation_type:
            conditions.append("operation_type = ?")
            params.append(operation_type)
        
        if success_only:
            conditions.append("success = 1")
        
        if start_time:
            conditions.append("timestamp >= ?")
            params.append(start_time.isoformat())
        
        if end_time:
            conditions.append("timestamp <= ?")
            params.append(end_time.isoformat())
        
        where_clause = "WHERE " + " AND ".join(conditions) if conditions else ""
        
        query = f"""
            SELECT id, expression, result, operation_type, success, error_message,
                   timestamp, duration, input_hash, metadata
            FROM calculations
            {where_clause}
            ORDER BY timestamp DESC
            LIMIT ? OFFSET ?
        """
        
        params.extend([limit, offset])
        
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.execute(query, params)
            records = []
            
            for row in cursor.fetchall():
                record = CalculationRecord(
                    id=row[0],
                    expression=row[1],
                    result=row[2],
                    operation_type=row[3],
                    success=bool(row[4]),
                    error_message=row[5],
                    timestamp=datetime.fromisoformat(row[6]),
                    duration=row[7],
                    input_hash=row[8],
                    metadata=json.loads(row[9]) if row[9] else {}
                )
                records.append(record)
            
            return records
    
    def get_recent_records(self, count: int = 10) -> List[CalculationRecord]:
        """获取最近的记录"""
        with self._lock:
            return self.memory_records[-count:] if self.memory_records else []
    
    def search_records(self, query: str, limit: int = 50) -> List[CalculationRecord]:
        """搜索记录"""
        search_query = f"%{query}%"
        
        sql = """
            SELECT id, expression, result, operation_type, success, error_message,
                   timestamp, duration, input_hash, metadata
            FROM calculations
            WHERE expression LIKE ? OR result LIKE ?
            ORDER BY timestamp DESC
            LIMIT ?
        """
        
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.execute(sql, [search_query, search_query, limit])
            records = []
            
            for row in cursor.fetchall():
                record = CalculationRecord(
                    id=row[0],
                    expression=row[1],
                    result=row[2],
                    operation_type=row[3],
                    success=bool(row[4]),
                    error_message=row[5],
                    timestamp=datetime.fromisoformat(row[6]),
                    duration=row[7],
                    input_hash=row[8],
                    metadata=json.loads(row[9]) if row[9] else {}
                )
                records.append(record)
            
            return records
    
    def get_statistics(self, days: int = 30) -> Dict[str, Any]:
        """获取统计信息"""
        start_time = datetime.now() - timedelta(days=days)
        
        with sqlite3.connect(self.db_path) as conn:
            # 总计算次数
            total_count = conn.execute(
                "SELECT COUNT(*) FROM calculations WHERE timestamp >= ?",
                [start_time.isoformat()]
            ).fetchone()[0]
            
            # 成功率
            success_count = conn.execute(
                "SELECT COUNT(*) FROM calculations WHERE timestamp >= ? AND success = 1",
                [start_time.isoformat()]
            ).fetchone()[0]
            
            # 按操作类型统计
            operation_stats = conn.execute("""
                SELECT operation_type, COUNT(*), AVG(duration)
                FROM calculations 
                WHERE timestamp >= ?
                GROUP BY operation_type
            """, [start_time.isoformat()]).fetchall()
            
            # 平均执行时间
            avg_duration = conn.execute(
                "SELECT AVG(duration) FROM calculations WHERE timestamp >= ?",
                [start_time.isoformat()]
            ).fetchone()[0] or 0
            
            # 最常用的表达式
            popular_expressions = conn.execute("""
                SELECT expression, COUNT(*) as count
                FROM calculations 
                WHERE timestamp >= ? AND success = 1
                GROUP BY expression
                ORDER BY count DESC
                LIMIT 10
            """, [start_time.isoformat()]).fetchall()
            
            return {
                'period_days': days,
                'total_calculations': total_count,
                'successful_calculations': success_count,
                'success_rate': success_count / total_count if total_count > 0 else 0,
                'average_duration': avg_duration,
                'operation_statistics': {
                    op_type: {'count': count, 'avg_duration': avg_dur}
                    for op_type, count, avg_dur in operation_stats
                },
                'popular_expressions': [
                    {'expression': expr, 'count': count}
                    for expr, count in popular_expressions
                ],
                'cache_stats': self.result_cache.get_stats()
            }
    
    def cache_result(self, key: str, result: Any):
        """缓存计算结果"""
        self.result_cache.put(key, result)
    
    def get_cached_result(self, key: str) -> Optional[Any]:
        """获取缓存的结果"""
        return self.result_cache.get(key)
    
    def clear_cache(self):
        """清空缓存"""
        self.result_cache.clear()
    
    def cleanup_old_records(self, days: int = 365):
        """清理旧记录"""
        cutoff_time = datetime.now() - timedelta(days=days)
        
        with sqlite3.connect(self.db_path) as conn:
            deleted_count = conn.execute(
                "DELETE FROM calculations WHERE timestamp < ?",
                [cutoff_time.isoformat()]
            ).rowcount
            
            # 优化数据库
            conn.execute("VACUUM")
            
            return deleted_count
    
    def export_history(self, file_path: str, format: str = 'json',
                      start_time: Optional[datetime] = None,
                      end_time: Optional[datetime] = None):
        """导出历史记录"""
        records = self.get_records(
            limit=10000,
            start_time=start_time,
            end_time=end_time
        )
        
        file_path = Path(file_path)
        file_path.parent.mkdir(parents=True, exist_ok=True)
        
        if format.lower() == 'json':
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump([record.to_dict() for record in records], f, 
                         indent=2, ensure_ascii=False, default=str)
        
        elif format.lower() == 'csv':
            import csv
            with open(file_path, 'w', newline='', encoding='utf-8') as f:
                if records:
                    writer = csv.DictWriter(f, fieldnames=records[0].to_dict().keys())
                    writer.writeheader()
                    for record in records:
                        writer.writerow(record.to_dict())
        
        else:
            raise ValueError(f"不支持的导出格式: {format}")
    
    def import_history(self, file_path: str, format: str = 'json'):
        """导入历史记录"""
        file_path = Path(file_path)
        if not file_path.exists():
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        if format.lower() == 'json':
            with open(file_path, 'r', encoding='utf-8') as f:
                data = json.load(f)
                for record_data in data:
                    record = CalculationRecord.from_dict(record_data)
                    self.add_record(record)
        
        elif format.lower() == 'csv':
            import csv
            with open(file_path, 'r', encoding='utf-8') as f:
                reader = csv.DictReader(f)
                for row in reader:
                    record = CalculationRecord.from_dict(row)
                    self.add_record(record)
        
        else:
            raise ValueError(f"不支持的导入格式: {format}")